/*************************************************************************
	> File Name: myshell.c
	> Author: H.M. Lau
	> Mail: hm.lau@outlook.com
	> Created Time: 2023/08/25 10:41
 ************************************************************************/

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>

#define NUM 1024
#define ARGV_SIZE 32
#define SEP " "
#define ENV_SIZE 64
#define INPUT_REDIR 1
#define OUTPUT_REDIR 2
#define APPEND_REDIR 3
#define NONE_REDIR 0
#define FILENAME_SIZE 32

char cmd_line[NUM];
char *g_argv[ARGV_SIZE];
char g_env[ENV_SIZE];

int redir_status = NONE_REDIR;

char* CheckRedir(int *len)
{
    assert(len > 1);
    char *symbol = g_argv[*len - 2];
    if (strcmp(symbol, ">") == 0) {
        redir_status = OUTPUT_REDIR;
    } else if (strcmp(symbol, ">>") == 0) {
        redir_status = APPEND_REDIR;
    } else if (strcmp(symbol, "<") == 0) {
        redir_status = INPUT_REDIR;
    } else {
        return NULL;
    }
    char *filename;
    filename = malloc(FILENAME_SIZE);
    strcpy(filename, g_argv[*len - 1]);
    g_argv[*len - 2] = '\0';
    *len -= 2;
    return filename;
}

int main()
{
    while (1) {
        memset(cmd_line, '\0', sizeof cmd_line);
        printf("[root@localhost myShell]# ");    
        fflush(stdout);
        if (fgets(cmd_line, sizeof cmd_line, stdin) == NULL) continue;
        cmd_line[strlen(cmd_line) - 1] = '\0';
        int index = 0;
        g_argv[index] = strtok(cmd_line, SEP);
        if (strcmp(g_argv[0], "ls") == 0) g_argv[++index] = "--color=auto";
        while (g_argv[++index] = strtok(NULL, SEP));
        char *sep = CheckRedir(&index);
        if (strcmp(g_argv[0], "cd") == 0) {
            if (g_argv[1] != NULL) chdir(g_argv[1]);
            continue;
        }
        if (strcmp(g_argv[0], "export") == 0 && g_argv[1] != NULL) {
            strcpy(g_env, g_argv[1]);
            int res = putenv(g_env);
            if (res == 0) printf("Put environ %s success\n", g_argv[1]);
            else printf("Put environ fail\n");
            continue;
        }
        pid_t id = fork();
        if (id == 0) {
            if (sep) {
                int fd = -1;
                switch (redir_status) {
                    case INPUT_REDIR:
                        fd = open(sep, O_RDONLY);
                        dup2(fd, 0); 
                        break;
                    case OUTPUT_REDIR:
                        fd = open(sep, O_WRONLY | O_TRUNC | O_CREAT, 0666);
                        dup2(fd, 1); 
                        break;
                    case APPEND_REDIR:
                        fd = open(sep, O_WRONLY | O_APPEND | O_CREAT, 0666);
                        dup2(fd, 1);
                        break;
                    default:
                        printf("bug\n");
                        break;
                }
            }
            execvp(g_argv[0], g_argv);
            exit(1);
        }
        int status = 0;
        pid_t res = waitpid(-1, &status, 0);
        if (res > 0) printf("Child process exit code:%d\n", WEXITSTATUS(status));
    }
    return 0;
}
